class UCBSpline:
    def __init__(self, p):
        self.p = p      # tuple or list of control points (x, y)
        n = len(p) - 1     # control points p0 ~ pn, i.e., p[0] ~ p[n]
        m = 3 + n + 1      # knots vector u0 ~um, i.e., u[0] ~ u[m]
        self.m = m
        step = 1 / (m - 2 * 3)
        self.u = 3 * [0] + [_u * step for _u in range(m - 2 * 3 + 1)] + [1] * 3     # knots vector
        num = n + 1 - 3     # number of curve segments
        self.a1 = []
        self.a2 = []
        self.a3 = []
        self.a4 = []
        self.b1 = []
        self.b2 = []
        self.b3 = []
        self.b4 = []
        for i in range(num):
            self.a1.append((-p[i][0] + 3 * p[i + 1][0] - 3 * p[i + 2][0] + p[i + 3][0]) / 6.0)
            self.a2.append((3 * p[i][0] - 6 * p[i + 1][0] + 3 * p[i + 2][0]) / 6.0)
            self.a3.append((-p[i][0] + p[i + 2][0]) / 2.0)
            self.a4.append((p[i][0] + 4 * p[i + 1][0] + p[i + 2][0]) / 6.0)
            self.b1.append((-p[i][1] + 3 * p[i + 1][1] - 3 * p[i + 2][1] + p[i + 3][1]) / 6.0)
            self.b2.append((3 * p[i][1] - 6 * p[i + 1][1] + 3 * p[i + 2][1]) / 6.0)
            self.b3.append((-p[i][1] + p[i + 2][1]) / 2.0)
            self.b4.append((p[i][1] + 4 * p[i + 1][1] + p[i + 2][1]) / 6.0)
    def __call__(self, t):
        if t < 0 or t >= 1:
            return None
        else:
            for i in range(3, self.m - 3):
                if self.u[i] <= t and t < self.u[i + 1]:
                    break
        idx = i - 3
        a1 = self.a1[idx]
        a2 = self.a2[idx]
        a3 = self.a3[idx]
        a4 = self.a4[idx]
        b1 = self.b1[idx]
        b2 = self.b2[idx]
        b3 = self.b3[idx]
        b4 = self.b4[idx]
        t = (t - self.u[i]) / (self.u[i + 1] - self.u[i])
        return (a1 * t * t * t + a2 * t * t + a3 * t + a4, 
                b1 * t * t * t + b2 * t * t + b3 * t + b4, 255)    # 255 is the point's alpha value, to meet the TCAX's point structure requirement ((x, y, a), (x, y, a), ...)

num = 1000
step = 1 / num
points = []
#P = [(200, 500), (300, 100), (700, 100), (400, 500), (600, 700), (800, 400), (700, 200)]
P = [(68, 9.798,0.084), (68, 13.32,0.0897), (87, 13.355,0.08708), (106, 9.7991,0.0696), (106,14.277, 0/0753), (140,9.656, 0.061), (140, 12.465,0.0651)]
ucb = UCBSpline(P)
for i in range(num):
    t = i * step
    points.append(ucb(t))
